package io.yichen.usbflutter;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import com.hoho.android.usbserial.util.SerialInputOutputManager;
import io.yichen.usbflutter.usb.driver.UsbConnector;
import io.yichen.usbflutter.usb.driver.UsbEngine;
import io.yichen.usbflutter.usb.driver.UsbItem;
import io.yichen.usbflutter.usb.driver.UsbResult;

/** UsbflutterPlugin */
public class UsbflutterPlugin implements FlutterPlugin, MethodCallHandler {

  private static final String TAG = "UsbPlugin";

  private static final String FIND_DEVICES = "findDevices";
  private static final String CONNECT = "connectDevice";
  private static final String DISCONNECT = "disconnectDevice";
  private static final String DISCONNECT_ALL = "disconnectDeviceAll";

  private UsbEngine usbEngine;
  private MethodChannel methodChannel;

  private Handler mainLooper;
  private BroadcastReceiver broadcastReceiver;

  private Context context;


  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
    if (usbEngine == null) {
      usbEngine = new UsbEngine();
    }
    String method = call.method;
    Log.e(TAG, "onMethodCall: " + method);
    if (FIND_DEVICES.equals(method)) {
      scanDevice(result);
      return;
    }
    if (CONNECT.equals(method)) {
      connectDevice(call, result);
      return;
    }
    if (DISCONNECT.equals(method)) {
      disconnectDevice(call, result);
      return;
    }
    if (DISCONNECT_ALL.equals(method)) {
      disconnectDeviceAll(result);
    }
  }

  private void disconnectDeviceAll(MethodChannel.Result result) {
    usbEngine.disconnectAll();
    result.success(UsbResult.suc().toMap());
  }

  private void disconnectDevice(MethodCall call, MethodChannel.Result result) {
    Integer deviceId = call.argument("deviceId");
    if (deviceId == null) {
      result.success(UsbResult.err().msg("缺少设备id"));
      return;
    }
    usbEngine.disconnect(deviceId);
    result.success(UsbResult.suc().toMap());
  }

  /**
   * 连接设备
   *
   * @param call
   * @param result
   */
  private void connectDevice(MethodCall call, MethodChannel.Result result) {
    Integer deviceId = call.argument("deviceId");
    Integer baudRate = call.argument("baudRate");
    if (deviceId == null) {
      result.success(UsbResult.err().msg("缺少设备id").toMap());
      return;
    }
    if (baudRate == null) {
      result.success(UsbResult.err().msg("缺少波特率").toMap());
      return;
    }
    UsbResult usbResult = usbEngine.connect(context, deviceId, baudRate, new SerialInputOutputManager.Listener() {
      @Override
      public void onNewData(byte[] data) {
        Map<String, Object> map = new HashMap<>(2);
        map.put("deviceId", deviceId);
        map.put("data", data);
        mainLooper.post(() -> methodChannel.invokeMethod("onDeviceData", map));
      }

      @Override
      public void onRunError(Exception e) {
        Map<String, Object> map = new HashMap<>(1);
        map.put("deviceId", deviceId);
        mainLooper.post(() -> methodChannel.invokeMethod("onDeviceDisconnect", map));
        usbEngine.disconnect(deviceId);
      }
    });
    result.success(usbResult.toMap());
  }

  /**
   * 扫码设备
   *
   * @param result
   */
  private void scanDevice(MethodChannel.Result result) {
    List<UsbItem> list = usbEngine.refreshList(context);
    List<Map<String, Object>> datas = new ArrayList<>();
    if (list == null || list.isEmpty()) {
      result.success(datas);
      return;
    }
    for (UsbItem item : list) {
      Map<String, Object> map = new HashMap<>();
      UsbDevice device = item.device;
      map.put("deviceName", device.getDeviceName());
      map.put("deviceId", device.getDeviceId());
      map.put("deviceProtocol", device.getDeviceProtocol());
      map.put("productId", device.getProductId());
      map.put("productName", device.getProductName());
//            map.put("serialNumber", device.getSerialNumber());
      map.put("vendorId", device.getVendorId());
      map.put("driver", item.driver != null ? 1 : 0);
//                map.put("version", device.getVersion());
      datas.add(map);
    }
    result.success(datas);
  }

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
    context = binding.getApplicationContext();
    methodChannel = new MethodChannel(binding.getBinaryMessenger(), "yichen_usb");
    methodChannel.setMethodCallHandler(this);
    usbEngine = new UsbEngine();
    _init(binding);
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    binding.getApplicationContext().unregisterReceiver(broadcastReceiver);
    methodChannel.setMethodCallHandler(null);
    methodChannel = null;
    if (usbEngine != null) {
      usbEngine.destroy();
    }
    usbEngine = null;
  }

  private void _init(FlutterPluginBinding binding) {
    broadcastReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        if (UsbConnector.INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
          int usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
                  ? 1 : 0;
          Map<String, Object> map = new HashMap<>(1);
          map.put("permission", usbPermission);
          mainLooper.post(() -> methodChannel.invokeMethod("onUsbPermission", map));
        }
      }
    };
    binding.getApplicationContext().registerReceiver(broadcastReceiver,
            new IntentFilter(UsbConnector.INTENT_ACTION_GRANT_USB));
    mainLooper = new Handler(Looper.getMainLooper());
  }
}
